// bmp.c

#include "bmp.h"
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <linux/fb.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>

// framebuffer 全局状态
static int fb_fd = -1;
static struct fb_var_screeninfo vinfo;
static struct fb_fix_screeninfo finfo;
static uint8_t *fbp = NULL;
static long screensize = 0;

uint32_t convert_rgb(uint32_t color)
{
    uint8_t r = (color >> 16) & 0xFF;
    uint8_t g = (color >> 8) & 0xFF;
    uint8_t b = color & 0xFF;
    return (r << vinfo.red.offset) |
           (g << vinfo.green.offset) |
           (b << vinfo.blue.offset);
}

static int init_fb(void)
{
    if (fbp)
        return 0;
    fb_fd = open("/dev/fb0", O_RDWR);
    if (fb_fd < 0)
    {
        perror("open /dev/fb0");
        return -1;
    }
    if (ioctl(fb_fd, FBIOGET_FSCREENINFO, &finfo) < 0 ||
        ioctl(fb_fd, FBIOGET_VSCREENINFO, &vinfo) < 0)
    {
        perror("ioctl FBIOGET_*SCREENINFO");
        close(fb_fd);
        return -1;
    }
    screensize = vinfo.yres_virtual * finfo.line_length;
    fbp = mmap(NULL, screensize, PROT_READ | PROT_WRITE, MAP_SHARED, fb_fd, 0);
    if (fbp == MAP_FAILED)
    {
        perror("mmap framebuffer");
        close(fb_fd);
        return -1;
    }
    return 0;
}

int bmp_init(void)
{
    return init_fb();
}

/**
 * @brief 加载 BMP 并自动判断是否自顶向下排列
 * @param path BMP 路径
 * @param buf 输出像素缓冲
 * @param w 宽
 * @param h 高（正值）
 * @return is_top_down = 1 表示自顶向下；0 表示自底向上；-1 表示错误
 */
static int load_bmp(const char *path, uint8_t **buf, int *w, int *h)
{
    // printf("load bmp: %s\n", path);
    FILE *fp = fopen(path, "rb");
    if (!fp)
    {
        perror("fopen bmp");
        return -1;
    }

    uint16_t bfType;
    fread(&bfType, 2, 1, fp);
    if (bfType != 0x4D42)
    {
        fprintf(stderr, "Not a BMP file: %s\n", path);
        fclose(fp);
        return -1;
    }

    fseek(fp, 18, SEEK_SET);
    int width, height;
    fread(&width, 4, 1, fp);
    fread(&height, 4, 1, fp);

    int is_top_down = (height < 0);
    *w = width;
    *h = height > 0 ? height : -height;

    fseek(fp, 54, SEEK_SET);
    int row_bytes = ((*w * 3 + 3) & ~3);
    *buf = malloc((size_t)row_bytes * (*h));
    if (!*buf)
    {
        perror("malloc bmp buf");
        fclose(fp);
        return -1;
    }

    for (int y = 0; y < *h; y++)
    {
        fread(*buf + (size_t)row_bytes * y, 1, (size_t)row_bytes, fp);
    }

    fclose(fp);
    return is_top_down;
}

void draw_bitmap(const char *path, int x, int y, int width, int height)
{
    if (!fbp)
    {
        fprintf(stderr, "Error: framebuffer not initialized\n");
        return;
    }

    uint8_t *bmp_buf = NULL;
    int bmp_w = 0, bmp_h = 0;
    int is_top_down = load_bmp(path, &bmp_buf, &bmp_w, &bmp_h);
    if (is_top_down == -1)
    {
        printf("Error: load bmp failed\n");
        return;
    }

    int row_bytes = ((bmp_w * 3 + 3) & ~3);

    for (int j = 0; j < height; j++)
    {
        for (int i = 0; i < width; i++)
        {
            int src_x = i * bmp_w / width;
            int src_y = j * bmp_h / height;
            if (!is_top_down)
            {
                src_y = bmp_h - 1 - src_y;
            }

            uint8_t *pixel = bmp_buf + (size_t)src_y * row_bytes + src_x * 3;
            uint32_t color = (pixel[2] << 16) | (pixel[1] << 8) | pixel[0];

            long location = (x + i) * (vinfo.bits_per_pixel / 8) + (y + j) * finfo.line_length;
            *((uint32_t *)(fbp + location)) = color;
        }
    }

    free(bmp_buf);
}

void fill_color(uint32_t color, int start_x, int start_y, int width, int height)
{
    if (!fbp)
    {
        fprintf(stderr, "Error: framebuffer not initialized\n");
        return;
    }

    for (int j = 0; j < height; j++)
    {
        for (int i = 0; i < width; i++)
        {
            int x = start_x + i;
            int y = start_y + j;

            // 防止越界访问
            if (x >= vinfo.xres || y >= vinfo.yres)
                continue;

            long location = x * (vinfo.bits_per_pixel / 8) + y * finfo.line_length;
            *((uint32_t *)(fbp + location)) = color;
        }
    }
}


void draw_border(int x, int y, int width, int height, uint32_t color, int thickness)
{
    if (!fbp)
    {
        fprintf(stderr, "Error: framebuffer not initialized\n");
        return;
    }

    // 限制厚度不要超过一半区域尺寸
    if (thickness < 1)
        thickness = 1;
    if (thickness > width / 2)
        thickness = width / 2;
    if (thickness > height / 2)
        thickness = height / 2;

    for (int t = 0; t < thickness; t++)
    {
        // 上边框
        for (int i = 0; i < width; i++)
        {
            long location = (x + i) * (vinfo.bits_per_pixel / 8) + (y + t) * finfo.line_length;
            *((uint32_t *)(fbp + location)) = color;
        }

        // 下边框
        for (int i = 0; i < width; i++)
        {
            long location = (x + i) * (vinfo.bits_per_pixel / 8) + (y + height - 1 - t) * finfo.line_length;
            *((uint32_t *)(fbp + location)) = color;
        }

        // 左边框
        for (int j = 0; j < height; j++)
        {
            long location = (x + t) * (vinfo.bits_per_pixel / 8) + (y + j) * finfo.line_length;
            *((uint32_t *)(fbp + location)) = color;
        }

        // 右边框
        for (int j = 0; j < height; j++)
        {
            long location = (x + width - 1 - t) * (vinfo.bits_per_pixel / 8) + (y + j) * finfo.line_length;
            *((uint32_t *)(fbp + location)) = color;
        }
    }
}

void draw_dashed_border(int x, int y, int width, int height, uint32_t color, int thickness, int dash_len, int gap_len)
{
    if (!fbp)
    {
        fprintf(stderr, "Error: framebuffer not initialized\n");
        return;
    }

    // 限制 thickness 合理范围
    if (thickness < 1) thickness = 1;
    if (thickness > width / 2) thickness = width / 2;
    if (thickness > height / 2) thickness = height / 2;

    // 颜色转换（考虑 framebuffer 实际格式）
    uint32_t real_color = convert_rgb(color);

    int pattern = dash_len + gap_len;

    for (int t = 0; t < thickness; t++)
    {
        // 上边框
        for (int i = 0; i < width; i++)
        {
            if ((i % pattern) < dash_len)
            {
                int px = x + i;
                int py = y + t;
                if (px >= 0 && px < vinfo.xres && py >= 0 && py < vinfo.yres)
                {
                    long loc = px * (vinfo.bits_per_pixel / 8) + py * finfo.line_length;
                    *((uint32_t *)(fbp + loc)) = real_color;
                }
            }
        }

        // 下边框
        for (int i = 0; i < width; i++)
        {
            if ((i % pattern) < dash_len)
            {
                int px = x + i;
                int py = y + height - 1 - t;
                if (px >= 0 && px < vinfo.xres && py >= 0 && py < vinfo.yres)
                {
                    long loc = px * (vinfo.bits_per_pixel / 8) + py * finfo.line_length;
                    *((uint32_t *)(fbp + loc)) = real_color;
                }
            }
        }

        // 左边框
        for (int j = 0; j < height; j++)
        {
            if ((j % pattern) < dash_len)
            {
                int px = x + t;
                int py = y + j;
                if (px >= 0 && px < vinfo.xres && py >= 0 && py < vinfo.yres)
                {
                    long loc = px * (vinfo.bits_per_pixel / 8) + py * finfo.line_length;
                    *((uint32_t *)(fbp + loc)) = real_color;
                }
            }
        }

        // 右边框
        for (int j = 0; j < height; j++)
        {
            if ((j % pattern) < dash_len)
            {
                int px = x + width - 1 - t;
                int py = y + j;
                if (px >= 0 && px < vinfo.xres && py >= 0 && py < vinfo.yres)
                {
                    long loc = px * (vinfo.bits_per_pixel / 8) + py * finfo.line_length;
                    *((uint32_t *)(fbp + loc)) = real_color;
                }
            }
        }
    }
}
